#include <hdc/pci.h>

namespace JLOS::Kernel {
void printf(const char *str);
void printfHex(JLOS::uint8_t);
}

namespace JLOS::Hdc {
PeripheralComponentInterconnectDeviceDesriptor::PeripheralComponentInterconnectDeviceDesriptor()
{

}

PeripheralComponentInterconnectDeviceDesriptor::~PeripheralComponentInterconnectDeviceDesriptor()
{

}

PeripheralComponentInterconnectController::PeripheralComponentInterconnectController()
:dataPort(0xCFC), commandPort(0xCF8)
{

}

PeripheralComponentInterconnectController::~PeripheralComponentInterconnectController()
{

}

uint32_t PeripheralComponentInterconnectController::Read(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset)
{
    uint32_t id = 
        0x1 <<31
        | ((bus & 0xFF) << 16)
        | ((device & 0x1F) << 11)
        | ((function & 0x07) << 8)
        | (registeroffset & 0xFC);
    
    commandPort.Write(id);
    uint32_t result = dataPort.Read();
    return result >> (8 * (registeroffset % 4));
}

void PeripheralComponentInterconnectController::Write(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset,
    uint32_t value)
{
    uint32_t id = 
        0x1 <<31
        | ((bus & 0xFF) << 16)
        | ((device & 0x1F) << 11)
        | ((function & 0x07) << 8)
        | (registeroffset & 0xFC);
    
    commandPort.Write(id);
    dataPort.Write(value);
}

bool PeripheralComponentInterconnectController::DeviceHasFunctions(uint16_t bus, uint16_t device)
{
    return Read(bus, device, 0, 0x0E) & (1<<7);
}

void PeripheralComponentInterconnectController::SelectDrivers(DriverManager *driverManager)
{
    for(int bus = 0; bus < 8; bus++) {
        for(int device = 0; device < 32; device++) {
            int numFunctions = DeviceHasFunctions(bus, device) ? 8 : 1;
            for(int function = 0; function < numFunctions; function++) {
                PeripheralComponentInterconnectDeviceDesriptor dev = GetDeviceDescriptor(bus, device, function);

                if(dev.vendor_id == 0x0000 || dev.vendor_id == 0xFFFF) {
                    continue;
                }
                Kernel::printf("PCI BUS ");
                Kernel::printfHex(bus & 0xFF);

                Kernel::printf(", DEVICE ");
                Kernel::printfHex(device & 0xFF);

                Kernel::printf(", FUNCTION ");
                Kernel::printfHex(function & 0xFF);

                Kernel::printf(" = VENDOR ");
                Kernel::printfHex((dev.vendor_id & 0xFF00) >> 8);
                Kernel::printfHex(dev.vendor_id & 0xFF);

                Kernel::printf(", DEVICE ");
                Kernel::printfHex((dev.device_id & 0xFF00) >> 8);
                Kernel::printfHex(dev.device_id & 0xFF);
                Kernel::printf("\n");
            }
        }
    }
}

PeripheralComponentInterconnectDeviceDesriptor PeripheralComponentInterconnectController::GetDeviceDescriptor(uint16_t bus, uint16_t device, uint16_t function)
{
    PeripheralComponentInterconnectDeviceDesriptor result;
    result.bus = bus;
    result.device = device;
    result.function = function;

    result.vendor_id = Read(bus, device, function, 0x00);
    result.device_id = Read(bus, device, function, 0x02);

    result.class_id = Read(bus, device, function, 0x0b);
    result.subclass_id = Read(bus, device, function, 0x0a);
    result.interface_id = Read(bus, device, function, 0x09);

    result.revision = Read(bus, device, function, 0x08);
    result.interrupt = Read(bus, device, function, 0x3c);

    return result; 
}
}
